package eu.webhib.frmwrk.jsf.utils;

import java.util.*;
import org.apache.log4j.Logger;

import eu.webhib.frmwrk.domain.intf.BaseEntity;
import eu.webhib.frmwrk.service.utils.RangeData;

//
public abstract class PaginationHelper<T extends BaseEntity> {

   private static final Logger log = Logger.getLogger(PaginationHelper.class);
   private static String SESSION_ATTRIBUTE_NAME = "pageSessionData";
   private PaginationSessionData sessionData;
   private int totalCount;
   private List<T> model;

   public PaginationHelper(int pageSize, boolean isAlreadySessionScoped) {
      // handle session sensitive data over session if needed,
      // e.g. then PaginationHelper is not embedded into session scoped managed bean
      if (!isAlreadySessionScoped) {
         sessionData = (PaginationSessionData) JsfUtil.getSessionAttr(SESSION_ATTRIBUTE_NAME);
         if (sessionData == null) {
            sessionData = new PaginationSessionData();
            JsfUtil.setSessionAttribute(SESSION_ATTRIBUTE_NAME, sessionData);
            sessionData.pageSize = pageSize;
            sessionData.startIndex = 0;
         }
      } else {
         sessionData = new PaginationSessionData();
         sessionData.pageSize = pageSize;
         sessionData.startIndex = 0;
      }
   }

   public abstract RangeData<T> fetchRangeData(int startIndex, int size);

   private List<T> getModel() {
      log.trace("getModel");
      if (model == null) {
         RangeData<T> rangeData = fetchRangeData(sessionData.startIndex, sessionData.pageSize);
         sessionData.startIndex = rangeData.getStartIndex();
         totalCount = rangeData.getTotalCount();
         log.info("getModel: items fetched: " + rangeData.getItems().size());
         model = rangeData.getItems();
      }
      return model;
   }

   public int getTotalCount() {
      log.trace("getTotalCount");
      // totalCount depends on a valid model
      getModel();
      log.trace("getTotalCount: " + totalCount);
      return totalCount;
   }

   public List<T> getItems() {
      log.trace("getItems");
      return getModel();
   }

   public void invalidateModel() {
      // next call to getTotalCount() or getItems() forces database fetch
      model = null;
   }

   public int getPageSize() {
      return sessionData.pageSize;
   }

   public int getPageStartIndex() {
      return sessionData.startIndex;
   }

   public int getPageLastIndex() {
      int i = sessionData.startIndex + sessionData.pageSize - 1;
      if (i >= totalCount) {
         i = totalCount - 1;
      }
      if (i < 0) {
         i = 0;
      }
      return i;
   }

   public void gotoNextPage() {
      if (isHasNextPage()) {
         sessionData.startIndex += sessionData.pageSize;
      }
      invalidateModel();
   }

   public void gotoPreviousPage() {
      if (isHasPreviousPage()) {
         sessionData.startIndex -= sessionData.pageSize;
      }
      invalidateModel();
   }

   public boolean isHasNextPage() {
      return sessionData.startIndex + sessionData.pageSize < totalCount;
   }

   public boolean isHasPreviousPage() {
      return sessionData.startIndex > 0;
   }
}
